#ifndef REACTOR
#define REACTOR

#include <unordered_map>
#include "Epoller.hpp"
#include "Connection.hpp"

class Reactor
{
    static constexpr int MAXEVENTS = 1024;
    static constexpr int EPOLLTIMEOUT = 500;    // 毫秒
    static constexpr int EPOLLWAITTIMEOUT = -1; // 毫秒,先按照阻塞的方式来

    bool IsConnection(const int fd) const
    {
        return _connections.find(fd) != _connections.end();
    }

    bool IsConnection(std::shared_ptr<Connection> conn) const
    {
        return IsConnection(conn->GetSockFd());
    }

    bool IsConnectionEmpty() const
    {
        return _connections.empty();
    }

    bool IsConnectionExists(const int fd) const
    {
        return IsConnection(fd);
    }
    // 监听某个连接事件
    int LoopOnce(const int timeout = EPOLLTIMEOUT)
    {
        return _epoll_ptr->WaitEvent(_events, MAXEVENTS, timeout);
    }

public:
    // 添加新连接到_connections中,并注册到epoll中
    bool AddConnection(const std::shared_ptr<Connection> &conn)
    {
        LogModule::LOG(DEBUG) << "添加新连接函数";

        // 检测重复添加
        if (IsConnectionExists(conn->GetSockFd()))
        {
            LogModule::LOG(WARNING) << "重复添加连接";
            return false;
        }

        uint32_t events = conn->GetEvent();
        int fd = conn->GetSockFd();

        _epoll_ptr->AddEvent(fd, events);

        LogModule::LOG(DEBUG) << "添加新连接函数1";
        conn->SetOwner(this);
        _connections[fd] = conn;

        LogModule::LOG(DEBUG) << "添加新连接函数结束";

        return true;
    }

    void DelConnection(int sockfd)
    {
        _epoll_ptr->DelEvent(sockfd, 0);
        _connections.erase(sockfd);
        ::close(sockfd);
        LogModule::LOG(INFO) << "client quit: " << sockfd;
    }
    void PrintConnections() const
    {
        for (const auto &pair : _connections)
        {
            LogModule::LOG(INFO) << "fd: " << pair.first;
        }
    }

public:
    Reactor()
        : _epoll_ptr(std::make_unique<Epoll>()),
          _isrunning(false)
    {
    }
    void Loop()
    {
        // 存在连接了才能跑起来，封装一个检测函数
        if (IsConnectionEmpty())
            return;

        if (!_isrunning)
            _isrunning = true;

        while (_isrunning)
        {
            // 跑起来之后该干什么了呢？
            // 监听连接，接收数据，发送数据
            // 用一个专门的类来处理连接
            PrintConnections();

            int n = LoopOnce(EPOLLWAITTIMEOUT);

            if (n < 0)
                break;
            if (n == 0)
                continue;
            // 处理文件描述符，不同的fd应该做不同的任务，用一个函数来分配任务
            Dispatch(n);
        }
        _isrunning = false;
    }
    // 分发任务
    void Dispatch(int n)
    {
        LogModule::LOG(DEBUG) << "Dispatch(int n)";
        // n这个参数代表的是fd，需要检测是监听fd还是普通fd
        for (int i = 0; i < n; ++i)
        {
            // 检测是否存在于_connections中
            int sockfd = _events[i].data.fd;
            uint32_t revents = _events[i].events;
            // 将异常处理转化为IO错误
            // 将所有的IO异常，统一使用一个异常处理函数
            if (revents & EPOLLERR)
                revents |= (EPOLLIN | EPOLLOUT);

            if (revents & EPOLLHUP)
                revents |= (EPOLLIN | EPOLLOUT);

            // 读事件就绪
            if (revents & EPOLLIN)
            {
                // 不需要处理读事件异常
                // 不需要区分是哪种套接字就绪，我们封装的是连接

                // 这个函数好像有点问题，加上判断不输出HandleRead()内的日志，也可能是添加连接的逻辑出了问题
                // 更正了，问题出在重复添加连接上
                if (IsConnectionExists(sockfd))
                    _connections[sockfd]->HandleRead();
            }

            // 写事件就绪
            if (revents & EPOLLOUT)
            {
                if (IsConnectionExists(sockfd))
                    _connections[sockfd]->HandleWrite();
            }
        }
    }
    void Stop()
    {
        if (_isrunning)
            _isrunning = false;
    }

    void EnableReadWrite(int sockfd, bool enableRead, bool enableWrite)
    {
        // 添加到连接中
        if (!IsConnectionExists(sockfd))
        {
            LogModule::LOG(WARNING) << sockfd << "conn is exists";
        }

        // 修改当前fd的关心事件
        uint32_t new_events = (EPOLLIN | EPOLLET) | (enableRead ? EPOLLIN : 0) | (enableWrite ? EPOLLOUT : 0);
        _connections[sockfd]->SetEvent(new_events);

        // 将关心事件添加到内核中
        _epoll_ptr->AddEvent(sockfd, new_events);
    }
    ~Reactor() {}

private:
    // epoll模型
    std::unique_ptr<Epoll> _epoll_ptr;
    // 运行状态
    bool _isrunning;

    // 将关心的fd和Connection对象关联起来，将关心的事件封装在Connection对象中，通过fd寻找即可
    std::unordered_map<int, std::shared_ptr<Connection>> _connections;

    // 就绪的事件
    struct epoll_event _events[MAXEVENTS];
};

#endif